package io.jpress.pro.attachment.minio;

import com.jfinal.kit.LogKit;
import com.jfinal.kit.PathKit;
import io.jboot.app.config.JbootConfigManager;
import io.jboot.utils.FileUtil;
import io.jboot.utils.StrUtil;
import io.jboot.web.attachment.AttachmentContainer;
import io.minio.*;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;


/**
 * @author michael yang (fuhai999@gmail.com)
 */
public class MinioAttachmentContainer implements AttachmentContainer {

    private String basePath = PathKit.getWebRootPath();
    private MinioAttachmentConfig config = JbootConfigManager.me().get(MinioAttachmentConfig.class);


    public MinioAttachmentContainer() {

    }

    public MinioAttachmentContainer(String basePath, MinioAttachmentConfig config) {
        this.basePath = basePath;
        this.config = config;
    }

    @Override
    public String saveFile(File file) {
        if (!config.isEnable()) {
            return null;
        }
        String relativePath = getRelativePath(file);
        upload(relativePath, file);
        return relativePath;
    }


    @Override
    public String saveFile(File file, String toRelativePath) {
        if (!config.isEnable()) {
            return null;
        }
        upload(toRelativePath, file);
        return toRelativePath;
    }

    @Override
    public String saveFile(InputStream inputStream, String toRelativePath) {
        if (!config.isEnable()) {
            return null;
        }
        upload(toRelativePath, inputStream);
        return toRelativePath;
    }


    @Override
    public boolean deleteFile(String relativePath) {
        if (!config.isEnable()) {
            return false;
        }
        relativePath = removeFirstFileSeparator(relativePath);
        MinioClient minioClient = createMinioClient();
        try {
            minioClient.removeObject(RemoveObjectArgs.builder().bucket(config.getBucketname()).object(relativePath).build());
        } catch (Exception ex) {
            LogKit.error(ex.toString(), ex);
        }
        return true;
    }


    @Override
    public File getFile(String relativePath) {
        if (!config.isEnable()) {
            return null;
        }
        File localFile = new File(basePath, relativePath);
        if (localFile.exists()) {
            return localFile;
        }
        if (download(relativePath, localFile)) {
            return localFile;
        }
        return null;
    }


    @Override
    public String getRelativePath(File file) {
        if (!config.isEnable()) {
            return null;
        }
        return FileUtil.removePrefix(file.getAbsolutePath(), basePath);
    }


    /**
     * 同步本地文件到Minio
     *
     * @param path
     * @param file
     * @return
     */
    public boolean upload(String path, File file) {
        if (StrUtil.isBlank(path)) {
            return false;
        }

        path = removeFirstFileSeparator(path);
        path = path.replace('\\', '/');

        MinioClient minioClient = createMinioClient();

        try {
            ObjectWriteResponse objectWriteResponse = minioClient.uploadObject(UploadObjectArgs.builder()
                    .bucket(config.getBucketname())
                    .object(path)
                    .filename(file.getAbsolutePath()).build());
            return objectWriteResponse != null;

        } catch (Exception e) {
            LogKit.error("MinioClient upload error!!!", e);
            return false;
        }
    }


    public boolean upload(String path, InputStream inStream) {
        if (StrUtil.isBlank(path)) {
            return false;
        }

        path = removeFirstFileSeparator(path);
        path = path.replace('\\', '/');

        MinioClient minioClient = createMinioClient();

        try {
            ObjectWriteResponse objectWriteResponse = minioClient.uploadObject(UploadObjectArgs.builder().bucket(config.getBucketname())
                    .object(path).build());
            return objectWriteResponse != null;
        } catch (Exception e) {
            LogKit.error("MinioClient upload error!!!", e);
            return false;
        }
    }


    /**
     * 如果文件以 / 或者 \ 开头，去除 / 或 \ 符号
     */
    private static String removeFirstFileSeparator(String path) {
        while (path.startsWith("/") || path.startsWith("\\")) {
            path = path.substring(1);
        }
        return path;
    }

    /**
     * 下载 Minio到本地
     *
     * @param path
     * @param toFile
     * @return
     */
    public boolean download(String path, File toFile) {
        if (StrUtil.isBlank(path)) {
            return false;
        }
        path = removeFirstFileSeparator(path);
        MinioClient minioClient = createMinioClient();
        try {
            if (!toFile.getParentFile().exists() && !toFile.getParentFile().mkdirs()) {
                LogKit.error("dir can not create! {}", toFile.getParentFile());
            }

            if (!toFile.exists() && !toFile.createNewFile()) {
                LogKit.error("file can not create! {}", toFile);
            }

            try (InputStream stream = minioClient.getObject(GetObjectArgs.builder()
                    .bucket(config.getBucketname())
                    .object(path).build())
                 ; FileOutputStream fStream = new FileOutputStream(toFile);) {
                byte[] buffer = new byte[8 * 1024];
                int bytesRead;
                while ((bytesRead = stream.read(buffer)) != -1) {
                    fStream.write(buffer, 0, bytesRead);
                }
            }
            return true;
        } catch (Exception e) {
            LogKit.error("MinioClient download error!!!  path:" + path + "   toFile:" + toFile, e);
            if (toFile.exists() && !toFile.delete()) {
                LogKit.error("file can not delete! {}", toFile);
            }
            return false;
        }
    }


    private MinioClient createMinioClient() {
        String endpoint = config.getEndpoint();
        String accessKey = config.getAccessKey();
        String secretKey = config.getSecretKey();
        return MinioClient.builder()
                .endpoint(endpoint)
                .credentials(accessKey, secretKey)
                .build();
    }


}
